import type { Router } from 'vue-router';
import NProgress from 'nprogress';
import { useTitle } from '@vueuse/core';
import useUserStore from '@/store/user';
import { loginService, viewService } from '@great/services';
import { emitter, EmitterEvent, getDocumentTitle } from '@great/utils';
import { checkPermi } from '@/utils/ruoyi';

NProgress.configure({
    speed: 300,
    showSpinner: false
});

enum RouteNameEnum {
    /**
     * 登录页
     */
    LOGIN = 'login'
}

const ROUTER_WHITE_LIST: string[] = [];

export default function createPermissionGuard(router: Router) {
    const loadedPaths = new Set<string>();

    router.beforeEach(async (to, from, next) => {
        to.meta.loaded = loadedPaths.has(to.path);
        if (!to.meta.loaded) {
            NProgress.start();
        }

        const userStore = useUserStore();

        // 判断访问的页面是登陆页，如果有 Token 就不需要再去登录了
        if (to.name === RouteNameEnum.LOGIN) {
            if (userStore.token) return next(from.fullPath);
            return next();
        }

        // 判断访问页面是否在路由白名单地址中，如果存在直接放行
        if (ROUTER_WHITE_LIST.includes(to.name as RouteNameEnum) || to.path?.toString().startsWith('/test/')) {
            return next();
        }
        // 判断是否有 Token，没有重定向到 login 页面
        if (!userStore.token) {
            return next({
                name: RouteNameEnum.LOGIN,
                query: {
                    redirect: to.fullPath,
                    ...to.query
                },
                replace: true
            });
        }

        // 页面加载前获取的必要数据
        {
            // 收集需要执行的请求
            const requests: Array<any> = [];
            let userInfoRequest;
            let menuRequest;

            // 判断是否需要请求用户信息
            if (!userStore.userInfo) {
                userInfoRequest = loginService.getInfo();
                requests.push(userInfoRequest);
            }
            // 判断是否需要请求菜单
            if (!userStore.menu) {
                menuRequest = loginService.getRouters();
                requests.push(menuRequest);
            }
            // 并行执行所有需要的请求
            if (requests.length > 0) {
                await Promise.all(requests);
                // 处理用户信息结果
                if (userInfoRequest) {
                    const { data } = await userInfoRequest;
                    userStore.setUserInfo(data);
                }
                // 处理菜单结果
                if (menuRequest) {
                    const { data } = await menuRequest;
                    userStore.setMenu(data);
                }
            }
        }

        // 判断是否有权限
        if (to.name !== 'custom-page' && to.meta.permission) {
            if (!checkPermi([to.meta.permission])) {
                let toPath = '';
                if (userStore.menu && userStore.menu[0]) {
                    toPath =
                        userStore.menu[0].children && userStore.menu[0].children.length > 0
                            ? userStore.menu[0].children[0].path
                            : userStore.menu[0].path;
                    emitter.emit(EmitterEvent.REQUEST_FAILED, {
                        title: '无菜单权限，已为您重定向'
                    });
                } else {
                    toPath = '/404';
                    emitter.emit(EmitterEvent.REQUEST_FAILED, {
                        title: '无任何菜单权限，请检查配置'
                    });
                }
                return next(toPath);
            }
        }

        // 预加载客制化页面数据
        if (to.name === 'custom-page') {
            const result = await viewService.getDetailByCode(to.params.code as string);
            to.meta.initData = result.data;
            to.meta.title = result.data?.displayName || to.meta.title;
        }

        // 正常访问页面
        next();
    });

    router.afterEach(to => {
        loadedPaths.add(to.path);
        const title = to.meta?.customTitle || to.meta?.title;
        const pageTitle = getDocumentTitle(title as string);
        useTitle(pageTitle);
        NProgress.done();
    });

    router.onError(error => {
        NProgress.done();
        console.warn('路由错误 =>', error.message);
    });
}
